package com.tianji.learning.service.impl;

import cn.hutool.db.DbRuntimeException;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.util.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author 林云
 * @since 2023-08-10
 */
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;

    private final CourseClient courseClient;

    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 查询当前用户指定课程的学习进度
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.查询课表id（用于赋值给学习记录表的lesson_id）
        LearningLesson lesson = lessonService.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId).one();
        if (lesson == null) {
            throw new BizIllegalException("该课程未加入课表");
        }
        //3.封装为vo
        //查询的有很多小节，所以是集合
        List<LearningRecord> recordList = this.lambdaQuery().eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getLessonId, lesson.getId()).list();
        List<LearningRecordDTO> dtoList = BeanUtils.copyList(recordList, LearningRecordDTO.class);
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(dtoList);
        //4.返回
        return dto;
    }

    /**
     * 提交学习记录
     */
    @Override
    public void addLearningRecord(LearningRecordFormDTO dto) {

        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.处理学习记录
        boolean isFinished = false; //该小节是否完成
        if (dto.getSectionType().equals(SectionType.EXAM)) {
            //2.1 提交考试记录
            isFinished = handleExamRecord(userId, dto);
        } else {
            //2.2 提交视频播放记录
            isFinished = handleVideoRecord(userId, dto);
        }
        // 本小节不是第一次学完不处理课表数据
        if (!isFinished) {
            return;
        }
        //3.处理课表(learning_lesson)数据
        handleLessonData(dto, isFinished);

    }

    /**
     * 处理提交考试记录
     */
    private boolean handleExamRecord(Long userId, LearningRecordFormDTO dto) {
        //转换成po
        LearningRecord learningRecord = BeanUtils.copyBean(dto, LearningRecord.class);
        learningRecord.setUserId(userId);
        learningRecord.setFinished(true);//考试默认完成该章节
        learningRecord.setFinishTime(dto.getCommitTime());
        //考试直接保存到learning_record
        boolean result = this.save(learningRecord);
        if (!result) {
            throw new DbException("新增考试记录失败");
        }
        return true;//考试完代表一定完成了该节
    }

    /**
     * 处理提交视频播放记录
     */
    private boolean handleVideoRecord(Long userId, LearningRecordFormDTO dto) {
        //1. 查询旧的学习记录
        LearningRecord record = queryOldRecord(dto.getLessonId(), dto.getSectionId()); //改造成查缓存
        /*LearningRecord record = this.lambdaQuery().eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getLessonId, dto.getLessonId())
                .eq(LearningRecord::getSectionId, dto.getSectionId())
                .one();*/
        //2. 判断学习记录是否存在
        if (record == null) {
            //2.1 不存在则新增一条视频播放记录
            LearningRecord learningRecord = BeanUtils.copyBean(dto, LearningRecord.class);
            learningRecord.setUserId(userId);
            boolean result = this.save(learningRecord);
            if (!result) {
                throw new DbException("新增播放视频记录失败");
            }
            return false;
        }
        //2.2 存在则更新记录
        //判断本小节是否为第一次学完(未完成且进度条超过一半，认定为不是第一次看完)
        boolean isFinished = !record.getFinished() && dto.getMoment() * 2 >= dto.getDuration();
        //需要设置的字段: moment，但是finish和finishTime需要根据isFinished判断
        if (!isFinished) {//新增的缓存逻辑：如果不是第一次学完，则添加到延时任务中，避免多次查询DB
            LearningRecord recordTask = new LearningRecord();
            recordTask.setLessonId(dto.getLessonId());
            recordTask.setSectionId(dto.getSectionId());
            recordTask.setMoment(dto.getMoment());
            recordTask.setFinished(record.getFinished());
            taskHandler.addLearningRecordTask(recordTask);
            return false;//isFinished是false，所以这个也一定为false，下面的程序就不运行了，交给缓存走
        }
        boolean result = this.lambdaUpdate()
                .set(LearningRecord::getMoment, dto.getMoment())
                .set(isFinished, LearningRecord::getFinished, true)
                .set(isFinished, LearningRecord::getFinishTime, dto.getCommitTime())
                .eq(LearningRecord::getId, record.getId())//刚才查出旧的记录了，可以用主键来更新
                .update();
        if (!result) {
            throw new DbException("更新播放视频记录失败");
        }

        //如果不走缓存，说明缓存已失效，则清理缓存
        taskHandler.cleanRecordCache(dto.getLessonId(),dto.getSectionId());

        return isFinished;
    }

    /**
     * 查询旧的学习记录(缓存版)
     */
    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        //从缓存中读取lessonRecord
        LearningRecord record = taskHandler.readRecordCache(lessonId, sectionId);
        //命中直接返回
        if (record != null) {
            return record;
        }
        //缓存中没有？从数据库中查询
        LearningRecord dbRecord = this.lambdaQuery().eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId)
                .one();
        //返回空
        if (dbRecord == null) {
            return null;
        }
        //放入缓存中
        taskHandler.writeRecordCache(dbRecord);
        return dbRecord;
    }

    /**
     * 处理课表数据
     */
    private void handleLessonData(LearningRecordFormDTO dto, boolean isFinished) {

        //1.查询课表
        LearningLesson lesson = lessonService.getById(dto.getLessonId());
        if (lesson == null) {
            throw new BizIllegalException("课表不存在");
        }
        //2.判断是不是第一次学完
        boolean allFinished = false;
        if (isFinished) {//2.1判断所有小节是否学完
            //2.2需要远程调用courseClient
            CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (cInfo == null) {
                throw new BizIllegalException("课程不存在");
            }
            Integer sections = lesson.getLearnedSections();
            allFinished = sections + 1 >= cInfo.getSectionNum();
        }
        //3.更新课表（第一次学完才更新课表）
        lessonService.lambdaUpdate()
                .set(lesson.getStatus() == LessonStatus.NOT_BEGIN, LearningLesson::getStatus, LessonStatus.LEARNING)
                .set(allFinished, LearningLesson::getStatus, LessonStatus.FINISHED)
                .set(LearningLesson::getLatestSectionId, dto.getSectionId())
                .set(LearningLesson::getLatestLearnTime, dto.getCommitTime())
//                .setSql(isFinished, "learned_sections = learned_sections + 1")
                .setSql("learned_sections = learned_sections + 1")
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

}
